﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;

using GameOfLife.Cell;
//using GameOfLife.Drawing;

namespace GameOfLife.GameManager
{
    public class Game
    {
        private int width;
        private int height;
        private Canvas gameCanvas;
        private Cell.Cell[,] cells;

        public Game(int width, int height, Canvas canvas)
        {
            this.width = width;
            this.height = height;
            gameCanvas = canvas;
            cells = new Cell.Cell[width, height];
        }

        public Game(Canvas canvas)
        {
            gameCanvas = canvas;
            width = (int)(gameCanvas.Width / Cell.Cell.Size);
            height = (int)(gameCanvas.Height / Cell.Cell.Size);
            cells = new Cell.Cell[width, height];
        }

        public void DrawGrid(Canvas canvas, int size)
        {
            Line line;

            int counterHorizontal = (int)(canvas.Height / size);
            int counterVertical = (int)(canvas.Width / size);
            SolidColorBrush lineColor = new SolidColorBrush(Colors.Black);

            for (int i = 0; i <= counterHorizontal; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = (int)canvas.Width,
                    Y1 = 0,
                    Y2 = 0,
                    StrokeThickness = 2
                };


                canvas.Children.Add(line);
                Canvas.SetLeft(line, 0);
                Canvas.SetTop(line, size * i);
            }


            for (int i = 0; i <= counterVertical; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = 0,
                    Y1 = 0,
                    Y2 = (int)canvas.Height,
                    StrokeThickness = 2
                };

                canvas.Children.Add(line);
                Canvas.SetLeft(line, size * i);
                Canvas.SetTop(line, 0);
            }
        }
        public void DrawGrid(int size)
        {
            Line line;

            int counterHorizontal = (int)(gameCanvas.Height / size);
            int counterVertical = (int)(gameCanvas.Width / size);
            SolidColorBrush lineColor = new SolidColorBrush(Colors.Black);

            for (int i = 0; i <= counterHorizontal; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = (int)gameCanvas.Width,
                    Y1 = 0,
                    Y2 = 0,
                    StrokeThickness = 2
                };


                gameCanvas.Children.Add(line);
                Canvas.SetLeft(line, 0);
                Canvas.SetTop(line, size * i);
            }


            for (int i = 0; i <= counterVertical; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = 0,
                    Y1 = 0,
                    Y2 = (int)gameCanvas.Height,
                    StrokeThickness = 2
                };

                gameCanvas.Children.Add(line);
                Canvas.SetLeft(line, size * i);
                Canvas.SetTop(line, 0);
            }
        }

        public void DrawGrid()
        {
            int size = Cell.Cell.Size;
            Line line;

            int counterHorizontal = (int)(gameCanvas.Height / size);
            int counterVertical = (int)(gameCanvas.Width / size);
            SolidColorBrush lineColor = new SolidColorBrush(Colors.Black);

            for (int i = 0; i <= counterHorizontal; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = (int)gameCanvas.Width,
                    Y1 = 0,
                    Y2 = 0,
                    StrokeThickness = 0.3
                };


                gameCanvas.Children.Add(line);
                Canvas.SetLeft(line, 0);
                Canvas.SetTop(line, size * i);
            }


            for (int i = 0; i <= counterVertical; i++)
            {
                line = new Line
                {
                    Stroke = lineColor,
                    X1 = 0,
                    X2 = 0,
                    Y1 = 0,
                    Y2 = (int)gameCanvas.Height,
                    StrokeThickness = 0.3
                };

                gameCanvas.Children.Add(line);
                Canvas.SetLeft(line, size * i);
                Canvas.SetTop(line, 0);
            }
        }

        private void DrawCell(int x, int y)
        {
            int size = Cell.Cell.Size;

            Rectangle tmpRectangle = new Rectangle
                                         {
                                             Width = size,
                                             Height = size,
                                             Fill = new SolidColorBrush(Colors.Yellow),
                                             StrokeThickness = 3
                                         };

            gameCanvas.Children.Add(tmpRectangle);
            Canvas.SetLeft(tmpRectangle, size * x);
            Canvas.SetTop(tmpRectangle, size * y);
        }

        private void DrawCell(int x, int y, CellState cellCellState)
        {
            int size = Cell.Cell.Size;

            Rectangle tmpRectangle = new Rectangle
            {
                Width = size,
                Height = size,
                StrokeThickness = 3
            };

            if (cellCellState == CellState.Alive)
                tmpRectangle.Fill = new SolidColorBrush(Colors.Yellow);

            gameCanvas.Children.Add(tmpRectangle);
            Canvas.SetLeft(tmpRectangle, size * x);
            Canvas.SetTop(tmpRectangle, size * y);
        }

        public void GenerateCells()
        {
            //width = 100
            //height = 60

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {


                    cells[i, j] = new Cell.Cell(i, j, CellState.Dead);

                    var foo = cells[i, j].Rectangle;

                    gameCanvas.Children.Add(foo);
                    cells[i, j].Index = gameCanvas.Children.IndexOf(foo);
                    Canvas.SetLeft(foo, Cell.Cell.Size * i);
                    Canvas.SetTop(foo, Cell.Cell.Size * j);
                }
            }

            CreateInitialPattern();
        }

        private void CreateInitialPattern()
        {
            int numberOfCells = 30;
            Random rnd1 = new Random();

            for (int i = 0; i < numberOfCells; i++)
            {
                var cell = cells[rnd1.Next(45, 55), rnd1.Next(25, 35)];

                cell.CurrentCellState = CellState.Alive;

                //looking for a rectangle in children in gamecanvas

                ((Rectangle)gameCanvas.Children[cell.Index]).Fill = new SolidColorBrush(Colors.Yellow);

            }
        }

        public void Run()
        {
            CalculateNextGeneration();
            RefreshGame();
        }

        private void CalculateNextGeneration()
        {

            /*
 * link: http://en.wikipedia.org/wiki/Conway's_Game_of_Life
 * Rules
             
    1.  Any live cell with fewer than two live neighbours dies, as if caused by under-population.
    2.  Any live cell with more than three live neighbours dies, as if by overcrowding.
    3.  Any live cell with two or three live neighbours lives on to the next generation.
    4.  Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
 */

            Cell.Cell tmpCell;
            int tmpX, tmpY;

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    tmpCell = cells[i, j];

                    tmpCell.CellsAroundCounter = 0;
                    tmpX = (int)cells[i, j].Position.X;
                    tmpY = (int)cells[i, j].Position.Y;

                    //if(ObtainCellState(tmpX,tmpY)==CellState.Live)
                    //tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX - 1, tmpY + 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX, tmpY + 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX + 1, tmpY + 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX + 1, tmpY) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX + 1, tmpY - 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX, tmpY - 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX - 1, tmpY - 1) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;
                    if (ObtainCellState(tmpX - 1, tmpY) == CellState.Alive)
                        tmpCell.CellsAroundCounter++;

                    if (tmpCell.CurrentCellState == CellState.Alive)
                    {
                        if (tmpCell.CellsAroundCounter < 2 || tmpCell.CellsAroundCounter > 3)
                            tmpCell.NextCellState = CellState.Dead;
                        else if (tmpCell.CellsAroundCounter == 2 || tmpCell.CellsAroundCounter == 3)
                            tmpCell.NextCellState = CellState.Alive;
                    }
                    else if (tmpCell.CurrentCellState == CellState.Dead)
                    {
                        if (tmpCell.CellsAroundCounter == 3)
                            tmpCell.NextCellState = CellState.Alive;
                    }

                }
            }
        }

        private void RefreshGame()
        {
            Cell.Cell tmpCell;

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    tmpCell = cells[i, j];

                    if (tmpCell.NextCellState == CellState.Alive && tmpCell.CurrentCellState == CellState.Dead)
                    {
                        ((Rectangle)gameCanvas.Children[tmpCell.Index]).Fill = new SolidColorBrush(Colors.Yellow);
                        tmpCell.CurrentCellState = tmpCell.NextCellState;
                    }
                    else if (tmpCell.NextCellState == CellState.Dead && tmpCell.CurrentCellState == CellState.Alive)
                    {
                        ((Rectangle)gameCanvas.Children[tmpCell.Index]).Fill = new SolidColorBrush(Colors.White);
                        tmpCell.CurrentCellState = tmpCell.NextCellState;
                    }
                }
            }
        }

        private CellState ObtainCellState(int x, int y)
        {
            CellState state = CellState.Dead;

            try
            {

                //if (x >= width)
                //    x = width - x;
                //else if (x < 0)
                //    x = width + x+1;

                /*
                if (x == 100)
                    x = 0;
                else if (x == -1)
                    x = 99;

                if (y == 60)
                    y = 0;
                else if (y == -1)
                    y = 59;                

                state = cells[x, y].CurrentCellState;
                */

                if (x == -1 || x == 100 || y == -1 || y == 60)
                    state = CellState.Dead;
                else
                    state = cells[x, y].CurrentCellState;
                /*
                if (x >= width && y >= height)
                {
                    x = width - x;
                    y = height - y;
                    state = cells[x, y].CurrentCellState;
                }
                else if (x >= width)
                {
                    x = width - x;
                    state = cells[x, y].CurrentCellState;
                }
                else if (y >= height)
                {
                    y = height - y;
                    state = cells[x, y].CurrentCellState;
                }
                else if (x < 0 && y < 0)
                {
                    x = width - x;
                    y = height - y;
                    state = cells[x, y].CurrentCellState;
                }
                else if (x < 0)
                {
                    x = width - x;
                    state = cells[x, y].CurrentCellState;
                }
                else if (y < 0)
                {
                    y = height - y;
                    state = cells[x, y].CurrentCellState;
                }
                else
                {
                    state = cells[x, y].CurrentCellState;
                }
                */
            }
            catch (IndexOutOfRangeException exc)
            {
                MessageBox.Show(exc.Message + "   x = " + x + " y = " + y);
            }
            return state;
        }

        private CellState ObtainCellState(Point position)
        {
            CellState state;
            int x = (int)position.X;
            int y = (int)position.Y;

            if (x >= width && y >= height)
            {
                x = width - x;
                y = height - y;
                state = cells[x, y].CurrentCellState;
            }
            else if (x >= width)
            {
                x = width - x;
                state = cells[x, y].CurrentCellState;
            }
            else if (y >= height)
            {
                y = height - y;
                state = cells[x, y].CurrentCellState;
            }
            else
            {
                state = cells[x, y].CurrentCellState;
            }

            return state;
        }
    }
}
